package com.project.study.farming.common.utils;

import cn.hutool.core.util.StrUtil;
import com.project.study.farming.common.anno.Id;
import com.project.study.farming.common.constant.Constant;
import com.project.study.farming.common.entity.dto.BaseOutDto;
import com.project.study.farming.common.entity.dto.Page;
import com.project.study.farming.common.entity.dto.TcCodeDto;
import com.project.study.farming.common.entity.po.UserPo;
import com.project.study.farming.common.exception.ServiceException;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 工具类
 * @since 2020-03-03
 */
public class CommonUtil {

    private CommonUtil(){}

    private static final Pattern PATTERN = Pattern.compile("[A-Z]");

    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final String IMAGE_MKDIR = "images";

    /**
     * 将原有的字符串加密成新的字符串
     * @param str 加密前字符串
     * @return 加密后字符串
     */
    public static String encryptStr(String str){
        try {
            String salt = "farming";
            String charSet = Constant.CHAT_SET.getMessage();
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] bytes = md5.digest(String.format("%s%s", str, salt).getBytes(charSet));
            return new String(bytes, charSet).substring(0, 10);
        }catch (Exception e){
            throw new ServiceException(String.format("密码加密异常%s", e));
        }
    }

    /**
     * 根据属性获取对应的值
     * @param field 属性对象
     * @param t 实体类
     * @param <T> 实体类类型
     * @return 属性对应的值
     */
    public static <T> Object getFieldValue(Field field, T t){
        String fieldName = field.getName();
        String methodName = String.format("get%s%s", fieldName.substring(0,1).toUpperCase(), fieldName.substring(1));
        try {
            Method method = t.getClass().getMethod(methodName);
            Object o = method.invoke(t);
            if(Objects.isNull(o)){
                return field.getType()== Integer.class ? 0 : null;
            }else{
                String oStr = o.toString();
                return field.getType()== Integer.class ? Integer.parseInt(oStr) : oStr;
            }
        }catch (Exception e){
            throw new ServiceException(String.format("执行get方法异常%s", e));
        }
    }

    /**
     * 判断该属性是不是主键，判断依据是有没有Id注解
     * @param field 属性
     * @return 是否是主键
     */
    public static boolean isMainKey(Field field){
        Id id = field.getAnnotation(Id.class);
        return Objects.nonNull(id);
    }

    /**
     * 将驼峰名的属性名转为数据库名
     * @param name 属性名
     * @return 数据库名
     */
    public static String getDataBaseName(String name){
        Matcher matcher = PATTERN.matcher(name);
        while (matcher.find()){
            String str = matcher.group();
            name = name.replace(str, String.format("_%s", str.toLowerCase()));
        }
        return name;
    }

    /**
     * 获取request对象
     * @return request对象
     */
    public static HttpServletRequest getRequest(){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        return ((ServletRequestAttributes)requestAttributes).getRequest();
    }

    /**
     * 保存登录信息
     * @param po 登录人信息
     */
    public static void saveDataToSession(UserPo po){
        HttpServletRequest request = getRequest();
        HttpSession session = request.getSession();
        session.setAttribute(Constant.CUR_USER.name(), po);
    }

    /**
     * 获取当前人信息
     * @return 当前人信息
     */
    public static UserPo getUser(){
        return (UserPo) Objects.requireNonNull(getRequest()).getSession().getAttribute(Constant.CUR_USER.name());
    }

    /**
     * 退出登录
     */
    public static void logout(){
        getRequest().getSession().removeAttribute(Constant.CUR_USER.name());
    }

    /**
     * 格式化日期
     * @param localDateTime 日期
     * @return 日期的yyy-MM-dd HH:mm:ss形式
     */
    public static String formatLocalDateTime(LocalDateTime localDateTime){
        return DATE_TIME_FORMATTER.format(localDateTime);
    }

    /**
     * 将t转为字符串
     * @param t 入参
     * @param <T> 入参类型
     * @return 字符串
     */
    public static <T> String getString(T t){
        return Objects.isNull(t) ? StrUtil.EMPTY : t.toString();
    }

    /**
     * 通用返回方法-不分页
     * @param viewName 视图名
     * @param t 返回数据
     * @param <T> 泛型
     * @return 返回模型视图
     */
    public static <T> ModelAndView success(String viewName, T t){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName(viewName);
        modelAndView.addObject("res", BaseOutDto.success(t));
        return modelAndView;
    }

    /**
     * 通用返回方法-分页
     * @param viewName 视图名
     * @param pageNum 当前页
     * @param <T> 泛型
     * @return 返回模型视图
     */
    public static <T> ModelAndView successSelect(String viewName, List<T> list, Integer pageNum){
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName(viewName);
        modelAndView.addObject("res", BaseOutDto.success(Objects.isNull(pageNum) ? new Page<>(list) : new Page<>(pageNum, list)));
        return modelAndView;
    }

    /**
     * 上传文件
     * @param filePath 文件绝对路径
     * @param file 文件
     * @return 文件相对路径
     */
    public static String upload(String filePath, MultipartFile file){
        String filename = file.getOriginalFilename();
        assert filename != null;
        filename = String.format("%s%s", System.currentTimeMillis(), filename);
        String newFile = String.format("%s%s%s", filePath, File.separator, filename);
        File filepath = new File(filePath, filename);
        File parent = filepath.getParentFile();
        if (!parent.exists()) {
            if(!parent.mkdirs()){
                throw new ServiceException("上传文件时创建文件夹错误！！！");
            }
        }
        try {
            file.transferTo(new File(newFile));
        } catch (IOException e) {
            throw new ServiceException(String.format("上传文件发生错误：%s", e.getMessage()));
        }
        String[] data = newFile.split(IMAGE_MKDIR);
        return String.format("%s%s", IMAGE_MKDIR, data[data.length - 1]).replaceAll("\\\\", "/");
    }

    /**
     * 将map转换成对象
     * @param target map数据
     * @param cls 对象类对象
     * @param <T> 对象类型
     * @return 对象实例
     */
    public static <T> T transTo(Map<String, Object> target, Class<T> cls){
        if(Objects.isNull(target)){
            return null;
        }
        try {
            T t = cls.newInstance();
            target.forEach((key, value) -> {
                String methodName =  String.format("set%s%s", key.substring(0,1).toUpperCase(), key.substring(1));
                if(Objects.nonNull(value)){
                    try {
                        Class<?> fieldType = cls.getDeclaredField(key).getType();
                        Class<?> valueType = value.getClass();
                        Method method = cls.getDeclaredMethod(methodName, fieldType);
                        if(valueType == fieldType){
                            method.invoke(t, value);
                        }else if(fieldType == Integer.class){
                            method.invoke(t, Integer.parseInt(value.toString()));
                        }else if(fieldType == String.class) {
                            method.invoke(t, value.toString());
                        }else if(valueType == Timestamp.class){
                            method.invoke(t, LocalDateTime.ofInstant(((Timestamp) value).toInstant(), ZoneId.systemDefault()));
                        }else if(fieldType == Double.class){
                            method.invoke(t, Double.parseDouble(value.toString()));
                        }
                    } catch (Exception e) {
                        throw new ServiceException(e.getMessage());
                    }
                }
            });
            return t;
        }catch (Exception e){
            throw new ServiceException(String.format("转换对象错误：%s", e.getMessage()));
        }
    }

    /**
     * 根据key，获取字典项值
     * @param dic 字典
     * @param key kry
     * @return 值
     */
    public static String getTcCodeValue(List<TcCodeDto> dic, Integer key){
        TcCodeDto dto = dic.stream().filter(d -> d.getCode().equals(key)).findFirst().orElse(null);
        if(Objects.isNull(dto)){
            return StrUtil.EMPTY;
        }
        return dto.getValue();
    }
}
